package main;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Toolkit;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;

public class Experimentation extends JPanel implements ActionListener, PropertyChangeListener {
	
	private JProgressBar experimentProgress;
	private JButton startButton;
	private TrialConductor trialTask;
	private static final long serialVersionUID = 1L;
	public static final ArrayList<Swarm_Agent> swarm = new ArrayList<Swarm_Agent>();
	private Artificial_Agent agent;
	private double[] densitiesToTest = new double[] {0.3, 0.4, 0.5};
	/*
	 * The double values represent the percent of the grid that will be filled with obstacles.
	 * 0.3 = 30%, 0.4 = 40%, 0.5 = 50%
	 */
	private static Grid grid = new Grid();
	/*
	 * 1st Dimension - Intelligence System Number
	 * 2nd Dimension - All Recorded Values
	 * 3rd Dimension - Map Type
	 */
	private static double[][][] intelligenceValues = new double[6][7][30];
	/*
	 * First Row = Range Number (1-30)
	 * Second Row = Exact Range
	 * Third Row = Control Variable
	 * Fourth Row = Object Density of Map
	 * Fifth Row = Number of Trials (1000)
	 */
	private static int[][] mapValues = new int[5][30];
	private static int[] trialValues = new int[100];
	
	class TrialConductor extends SwingWorker<Void, Void> 
	{	
		private int numTrialsTotal = intelligenceValues[0][0].length*intelligenceValues.length*trialValues.length, numTrialsDone = 0, mapNum = 0, step = 0;
		
		public Void doInBackground()
		{
			setProgress(numTrialsDone);
			for(int obstacleDensityChanges = 0; obstacleDensityChanges < densitiesToTest.length; obstacleDensityChanges++)
			{
                grid.changeObjectDensity(densitiesToTest[obstacleDensityChanges]);
                
                for (int range = 1; range <= 10; range++)
                {
	                int[] specificRangeAndControlInfo = selectEndPoints(range);
	                mapValues[0][mapNum] = range;
	                mapValues[1][mapNum] = specificRangeAndControlInfo[1];
	                mapValues[2][mapNum] = specificRangeAndControlInfo[0];
	                mapValues[3][mapNum] = (int)(Grid.getObstacleDensity()*100);
	                mapValues[4][mapNum] = trialValues.length;
	                
	                for (int numIntelligenceTested = 0; numIntelligenceTested < 6; numIntelligenceTested++)
	                {
	                	constructAgent(numIntelligenceTested);
	                	System.out.println("Map is "+mapNum+" and range is "+range+" and intelligence is "+numIntelligenceTested);
	                	for (int trials = 0; trials < trialValues.length; trials++)
	                	{
	                		step = 0;
	                		outerloop:
	                		while(true)
	                		{
	                			step++;
	                			if (numIntelligenceTested <= 2)
	                			{
	                				int count = 0;
	                				for(Swarm_Agent a: swarm)
	                				{
	               		    		 	a.update();
	               		    		 	if (a.getSuccess() == SearchStatus.SUCCESSFUL)
	               		    		 	{
	               		    		 	    trialValues[trials] = step;
	               		    		 		break outerloop;
	               		    		 	}
	               		    		 	if(a.getSuccess() == SearchStatus.FAILURE)
	               		    		 	{
	               		    		 		count++;
	               		    		 		if (count == Math.pow(numIntelligenceTested, 2)+numIntelligenceTested+2)
	               		    		 		{
	               		    		 			System.out.println("Failure at: "+numIntelligenceTested+" intelligence during map "+mapNum+".");
	               		    		 			trials--;
	               		    		 			numTrialsDone--;
	               		    		 			break outerloop;
	               		    		 		}
	               		    		 	}
	                				}
	                			}
	                			else
	                			{
	                				agent.update();
	                				if (agent.getSuccess() == SearchStatus.SUCCESSFUL)
	                				{
	                					trialValues[trials] = step;
	                					break outerloop;
	                				}
	                			}
	                		}
	                		grid.clearGrid(false);
	                		constructAgent(numIntelligenceTested);
	    	                numTrialsDone++;
	    	                setProgress((int)((((double)numTrialsDone)/numTrialsTotal)*100)); 
	                	}
	                	Arrays.sort(trialValues);
	                	recordNecessaryInfo(trialValues, numIntelligenceTested, mapNum);
	                }
	                mapNum++;
	                grid.clearGrid(true);
                }
			}
			return null;
		}
		public void done() 
		{
            Toolkit.getDefaultToolkit().beep();
            startButton.setEnabled(true);
            GUI.experimentBack.setEnabled(true);
            CSVFileCreator.createCSVFile(intelligenceValues, mapValues);
            setCursor(null);
        }
	}
	public Experimentation()
	{
		setLayout(null);
		setBounds(0,0, 1300, 600);
		setVisible(false);
		setBackground(new Color (191, 0, 255));
		
		JLabel title = new JLabel("<html><center><h1 style ='font-size: 100; color: #40FF00;'>Experiment Mode</h1>");
		title.setBounds(220, 15, 1000, 150);
		
		startButton = new JButton("Start Experiment");
        startButton.setActionCommand("start");
        startButton.addActionListener(this);
        startButton.setBounds(575, 324, 150, 50);
        startButton.setBackground(new Color (0,255,191));
        
        experimentProgress = new JProgressBar(0, 100);
        experimentProgress.setValue(0);
        experimentProgress.setStringPainted(true);
        experimentProgress.setBounds(400,254,500,50);
        experimentProgress.setForeground(new Color(255, 0, 191));
        experimentProgress.setBackground(new Color(255, 191, 0));
        
        add(startButton);
        add(experimentProgress);
        add(title);
	}
	
	 public void actionPerformed(ActionEvent evt) {
	        startButton.setEnabled(false);
	        GUI.experimentBack.setEnabled(false);
	        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
	        trialTask = new TrialConductor();
	        trialTask.addPropertyChangeListener(this);
	        trialTask.execute();
	    }
	 
	 public void propertyChange(PropertyChangeEvent evt) {
	        if ("progress" == evt.getPropertyName()) {
	            int progress = (Integer) evt.getNewValue();
	            experimentProgress.setValue(progress);
	        } 
	    }
	 private int[] selectEndPoints(int range)
	 {
		return grid.randomlySelectEndPoints(range);
	 }
	 private void constructAgent(int agentNum)
	 {
		 swarm.clear();
		 agent = null;
		 switch(agentNum)
		 {
		 case 0:
			 for (int current = 1; current <= 2; current++)
					swarm.add(new Swarm_Agent(current, grid.getStartLocation()));
			 break;
		 case 1:
			 for (int current = 1; current <= 4; current++)
					swarm.add(new Swarm_Agent(current, grid.getStartLocation()));
			 break;
		 case 2:
			 for (int current = 1; current <= 8; current++)
					swarm.add(new Swarm_Agent(current, grid.getStartLocation()));
			 break;
		 case 3: agent = new Artificial_Agent(grid.getStartLocation(), 2);
			 break;
		 case 4: agent = new Artificial_Agent(grid.getStartLocation(), 3);
			 break;
		 case 5: agent = new Artificial_Agent(grid.getStartLocation(), 4);
			 break;
		 }
	 }
	 public void recordNecessaryInfo(int[] rawData, int intelligenceType, int mapType)
	 {
		 double minimum, quartileOne, quartileTwo, quartileThree, maximum, average, standardDeviation;
		 minimum = rawData[0];
		 quartileOne = rawData[(int)((rawData.length-1)*.25)];
		 quartileTwo = rawData[(int)((rawData.length-1)*.5)];
		 quartileThree = rawData[(int)((rawData.length-1)*.75)];
		 maximum = rawData[rawData.length-1];
		 double sum = 0;
		 for(int index = 0; index < rawData.length; index++)
		 {
			 sum+=rawData[index]; 
		 }
		 average = sum/rawData.length;		 
		 sum = 0; 
		 for (int index = 0; index < rawData.length; index++)
		 {
			 sum+=Math.pow(rawData[index]-average,2);
		 }
		 standardDeviation = Math.sqrt(sum/rawData.length);
		 intelligenceValues[intelligenceType][0][mapType] = minimum;
		 intelligenceValues[intelligenceType][1][mapType] = quartileOne;
		 intelligenceValues[intelligenceType][2][mapType] = quartileTwo;
		 intelligenceValues[intelligenceType][3][mapType] = quartileThree;
		 intelligenceValues[intelligenceType][4][mapType] = maximum;
		 intelligenceValues[intelligenceType][5][mapType] = ((double)Math.round(average*100)/(double)100);
		 intelligenceValues[intelligenceType][6][mapType] = ((double)Math.round(standardDeviation*100)/(double)100);
	 }
}
